package com.atguigu.gmall.order.service.impl;
import java.util.Date;

import com.atguigu.gmall.msg.express.OrderExpressMsg;

import java.util.*;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.atguigu.gmall.cart.entity.CartInfo;
import com.atguigu.gmall.common.config.mq.service.MqService;
import com.atguigu.gmall.common.config.utils.UserAuthUtil;
import com.atguigu.gmall.common.constant.MqConst;
import com.atguigu.gmall.common.constant.RedisConst;
import com.atguigu.gmall.common.execption.GmallException;
import com.atguigu.gmall.common.result.Result;
import com.atguigu.gmall.common.result.ResultCodeEnum;
import com.atguigu.gmall.enums.OrderStatus;
import com.atguigu.gmall.enums.PaymentWay;
import com.atguigu.gmall.enums.ProcessStatus;
import com.atguigu.gmall.feign.cart.CartFeignClient;
import com.atguigu.gmall.feign.product.ProductSkuDetailFeignCilent;
import com.atguigu.gmall.feign.user.UserFeignCilent;
import com.atguigu.gmall.feign.ware.WareFeignCilent;
import com.atguigu.gmall.msg.OrderCreateMsg;
import com.atguigu.gmall.order.entity.OrderDetail;
import com.atguigu.gmall.order.entity.OrderInfo;
import com.atguigu.gmall.order.entity.OrderStatusLog;
import com.atguigu.gmall.order.mapper.OrderInfoMapper;
import com.atguigu.gmall.order.service.OrderBizService;
import com.atguigu.gmall.order.service.OrderDetailService;
import com.atguigu.gmall.order.service.OrderInfoService;
import com.atguigu.gmall.order.service.OrderStatusLogService;
import com.atguigu.gmall.order.vo.FromWareSkuMapVo;
import com.atguigu.gmall.order.vo.OrderConfirmVo;
import com.atguigu.gmall.order.vo.ToWareOrderSpiltVo;
import com.atguigu.gmall.order.vo.OrderSubmitVo;
import com.atguigu.gmall.user.entity.UserAddress;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;

import java.math.BigDecimal;
import java.util.concurrent.*;
import java.util.stream.Collectors;

/**
 * @Description
 * @Return
 * @Author https://weiranyi.github.io
 * @Date Created in 2022-12-21 09:06
 **/

@Slf4j
@Service
public class OrderBizServiceImpl implements OrderBizService {
    @Autowired
    CartFeignClient cartFeignClient;
    @Autowired
    ProductSkuDetailFeignCilent skuDetailFeignCilent;
    @Autowired
    UserFeignCilent userFeignCilent;
    @Autowired
    WareFeignCilent wareFeignCilent;
    @Autowired
    StringRedisTemplate stringRedisTemplate;
    @Autowired
    OrderInfoService orderInfoService;
    // 直接用mapper可以减少调用层级
    @Autowired
    OrderInfoMapper orderInfoMapper;
    @Autowired
    OrderDetailService orderDetailService;
    @Autowired
    OrderStatusLogService orderStatusLogService;
    @Autowired
    ThreadPoolExecutor threadPoolExecutor;
    @Autowired
    MqService mqService;

    @Override
    public OrderConfirmVo getOrderConfirmData() {
        OrderConfirmVo orderConfirmVo = new OrderConfirmVo();
        // 【异步调用前-得到老请求】解决异步调用情况下（不是同一线程了），传递老请求头数据
        RequestAttributes oldRequestAttributes = RequestContextHolder.getRequestAttributes();
        // 1.获取购物车选中商品列表
        CompletableFuture<List<OrderConfirmVo.CartCheckedVo>>
                checkedVoFuture = CompletableFuture.supplyAsync(() -> {
            // 【异步调用-存放老请求】
            RequestContextHolder.setRequestAttributes(oldRequestAttributes);
            List<CartInfo> cartInfos = cartFeignClient.getCheckedCartInfo().getData();
            List<OrderConfirmVo.CartCheckedVo> checkedVoList = cartInfos.stream().map(item -> {
                OrderConfirmVo.CartCheckedVo cartCheckedVo = new OrderConfirmVo.CartCheckedVo();
                cartCheckedVo.setSkuId(item.getSkuId());
                cartCheckedVo.setSkuName(item.getSkuName());
                cartCheckedVo.setImgUrl(item.getImgUrl());
                // 价格不走【购物车服务】，走【商品服务】
                BigDecimal price = skuDetailFeignCilent.getPrice(item.getSkuId()).getData();
                cartCheckedVo.setOrderPrice(price);
                cartCheckedVo.setSkuNum(item.getSkuNum());
                // 【第三方库存系统】商品是否有货【远程调用是否有货】
                String hasStock = wareFeignCilent.hasStock(item.getSkuId(), item.getSkuNum());
                cartCheckedVo.setHasStock(hasStock);
                return cartCheckedVo;
            }).collect(Collectors.toList());
            orderConfirmVo.setDetailArrayList(checkedVoList);
            // 【异步调用-删除老请求-防止OOM】
            RequestContextHolder.resetRequestAttributes();
            return checkedVoList;
        }, threadPoolExecutor);

        // 2.计算总数量
        CompletableFuture<Void> totalNumFuture = checkedVoFuture.thenAcceptAsync((checkedVoList) -> {
            Integer totalNum = checkedVoList.stream()
                    .map(cartInfo -> cartInfo.getSkuNum())
                    .reduce((x, y) -> x + y)
                    .get();
            orderConfirmVo.setTotalNum(totalNum);
        }, threadPoolExecutor);

        // 3.计算总价格
        CompletableFuture<Void> totalAmountFuture = checkedVoFuture.thenAcceptAsync((checkedVoList) -> {
            BigDecimal totalAmount = checkedVoList.stream()
                    .map(cartInfo -> cartInfo.getOrderPrice().multiply(new BigDecimal(cartInfo.getSkuNum())))
                    .reduce((x, y) -> x.add(y))
                    .get();
            orderConfirmVo.setTotalAmount(totalAmount);
        }, threadPoolExecutor);

        // 4.用户所有收货地址
        CompletableFuture<Void> userAddressFuture = CompletableFuture.runAsync(() -> {
            // 【异步调用-存放老请求】
            RequestContextHolder.setRequestAttributes(oldRequestAttributes);
            Result<List<UserAddress>> userAddress = userFeignCilent.getUserAddress();
            orderConfirmVo.setUserAddressList(userAddress.getData());
            // 【异步调用-移除老请求】
            RequestContextHolder.resetRequestAttributes();
        }, threadPoolExecutor);
        // 5.流水号【对接三方支付/物流等系统，防重令牌，追踪订单】
        String tradeNo = "GMALL_" + System.currentTimeMillis() + "_" + UserAuthUtil.getUserIdHeader();
        CompletableFuture<Void> tradeNoFuture = CompletableFuture.runAsync(() -> {
            orderConfirmVo.setTradeNo(tradeNo);
            // 6.向redis中放入防重令牌【防止重复】
            stringRedisTemplate.opsForValue().set(RedisConst.ORDER_TOKEN_NO_REPEAT + tradeNo, "1", 30, TimeUnit.MINUTES);
        }, threadPoolExecutor);
        CompletableFuture.allOf(totalNumFuture, totalAmountFuture, userAddressFuture, tradeNoFuture).join();
        return orderConfirmVo;
    }


    ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(16);

    // 事务的地方千万别异步，异步时事务就失效了
    @Transactional
    @Override
    public Long submitOrder(String tradeNo, OrderSubmitVo orderSubmitVo) {
        // 1.验证令牌
        String script = "if redis.call(\"get\",KEYS[1]) == ARGV[1]\n" +
                "then\n" +
                "    return redis.call(\"del\",KEYS[1])\n" +
                "else\n" +
                "    return 0\n" +
                "end";
        //验证+删除保证原子性
        Long execute = stringRedisTemplate.execute(
                new DefaultRedisScript<Long>(script, Long.class),
                Arrays.asList(RedisConst.ORDER_TOKEN_NO_REPEAT + tradeNo),
                "1");
        if (!execute.equals(1L)) {
            // 验证失败; redis不存在入场券了，拒绝重复提交
            throw new GmallException(ResultCodeEnum.ORDER_SUBMIT_REPEAT);
        }
        // 2.验证库存
        long countDifferentStock = orderSubmitVo.getOrderDetailList().stream()
                .filter(item -> {
                    // 查询库存【后端不相信前端传来的数据】
                    String hasStock = wareFeignCilent.hasStock(item.getSkuId(), item.getSkuNum());
                    return "0".equals(hasStock);
                }).count();
        if (countDifferentStock > 0) {
            // 存在没有库存的商品
            throw new GmallException(ResultCodeEnum.SKU_NO_STOCK);
        }
        // 3.验证价格
        long countDifferentPrice = orderSubmitVo.getOrderDetailList().stream()
                .filter(item -> {
                    BigDecimal price = skuDetailFeignCilent.getPrice(item.getSkuId()).getData();
                    return !(Math.abs(price.subtract(item.getOrderPrice()).doubleValue()) < 0.001);
                }).count();
        if (countDifferentPrice > 0) {
            throw new GmallException(ResultCodeEnum.SKU_PRICE_DIFF);
        }
        // 以上验证通过，证明前端数据合法=================================================
        // 1.保存订单信息->order_info
        OrderInfo orderInfo = prepareOrderInfo(tradeNo, orderSubmitVo);
        orderInfoService.save(orderInfo);
        Long id = orderInfo.getId(); //得到雪花算法生成的id
        // 2.保存明细信息->order_detail
        List<OrderDetail> orderDetails = prepareOrderDetail(orderInfo, orderSubmitVo);
        orderDetailService.saveBatch(orderDetails);
        // 3.保存订单状态信息->oreder_status_log(订单状态流转表，方便溯源)
        OrderStatusLog orderStatusLog = prepareOrderStatus(orderInfo);
        orderStatusLogService.save(orderStatusLog);

        // 4.清除购物车 【MQ的方式：发消息(orderId,skuIdList)=>编程分布式而非响应式】
        RequestAttributes oldRequestAttributes = RequestContextHolder.getRequestAttributes();
        CompletableFuture.runAsync(() -> {
            RequestContextHolder.setRequestAttributes(oldRequestAttributes);
            List<Long> ids = orderSubmitVo.getOrderDetailList().stream()
                    .map(item -> item.getSkuId())
                    .collect(Collectors.toList());
            cartFeignClient.deleteSkuIds(ids);
            RequestContextHolder.resetRequestAttributes();
        }, threadPoolExecutor);

        // 【关闭订单业务】===========================================================================
        // 下单后MQ就会有消息了【给MQ说以下，这个单下好了，MQ去管理关单的消息】
        OrderCreateMsg createMsg = new OrderCreateMsg(id, orderInfo.getUserId());
        mqService.convertAndSend(MqConst.ORDER_EVENT_EXCHANGE, MqConst.ORDER_CREAT_RK, createMsg);
        //返回订单号
        return id;
    }

    /**
     * Cause: org.apache.shardingsphere.infra.exception.ShardingSphereException: Inline sharding algorithms expression `order-${user_id%2}-ds` and sharding column `user_id` not match.
     * 出现以上报错原因：传入userId是字符串类型，分片算法使用id取模的算法，就会提示主键id跟分片算法不匹配的错误
     *
     * @param userId
     * @param orderId
     * @return
     */
    @Override
    public OrderInfo getOrderInfo(String userId, Long orderId) {
        OrderInfo orderInfo = orderInfoService.getOne(new LambdaQueryWrapper<OrderInfo>()
                .eq(OrderInfo::getId, orderId)
                .eq(OrderInfo::getUserId, Long.valueOf(userId)));
        return orderInfo;
    }

    @Override
    public OrderInfo getOrderInfo(String tradeNo) {
        String[] str = tradeNo.split("_");
        // 1.得到用户id
        Long userId = Long.parseLong(str[str.length - 1]);
        // 2.tradeNo得在数据库做好索引
        OrderInfo orderInfo = orderInfoMapper.selectOne(new LambdaQueryWrapper<OrderInfo>()
                .eq(OrderInfo::getUserId, userId)
                .eq(OrderInfo::getOutTradeNo, tradeNo));
        return orderInfo;
    }

    @Override
    public void closeOrder(Long orderId, Long userId) {
        // 订单没支付，且已经过期
        ProcessStatus fromUnpaidStatus = ProcessStatus.UNPAID; //订单【当前状态】没支付
        ProcessStatus toClosedStatus = ProcessStatus.CLOSED; //订单状态【改为】关闭
        orderInfoMapper.updateOrderStatus(
                toClosedStatus.getOrderStatus().name(),
                toClosedStatus.name(),
                orderId,
                userId,
                Arrays.asList(fromUnpaidStatus.getOrderStatus().name()),
                Arrays.asList(fromUnpaidStatus.name()));
    }

    @Override
    public void changeOrderStatusToPayed(Long userId, Long orderId) {
        ProcessStatus fromUnpaidStatus = ProcessStatus.UNPAID; //订单【当前状态】没支付
        ProcessStatus fromCloseStatus = ProcessStatus.UNPAID; //订单【当前状态】没支付
        ProcessStatus toPyidStatus = ProcessStatus.PAID; //订单状态【改为】已经支付
        orderInfoMapper.updateOrderStatus(
                toPyidStatus.getOrderStatus().name(),
                toPyidStatus.name(),
                orderId,
                userId,
                Arrays.asList(fromUnpaidStatus.getOrderStatus().name(), fromCloseStatus.getOrderStatus().name()),
                Arrays.asList(fromUnpaidStatus.name(), fromCloseStatus.name()));
    }

    @Override
    public OrderInfo getOrderInfo(long orderId) {
        log.info("按照订单id：【{}】,全库全表扫描查找订单", orderId);
        OrderInfo orderInfo = orderInfoMapper.selectById(orderId);
        return orderInfo;
    }

    @Override
    public void updateOrderDeduceStatus(Long userId, Long orderId, String status) {
        ProcessStatus fromPyidStatus = ProcessStatus.PAID;
        ProcessStatus toStockTellUsStatus = null;
        switch (status) {
            case "DEDUCTED":
                toStockTellUsStatus = ProcessStatus.WAITING_DELEVER; //待发货
                break;
            case "OUT_OF_STOCK": // 超卖
                toStockTellUsStatus = ProcessStatus.WAITING_SCHEDULE;
                break;
            default:
                toStockTellUsStatus = ProcessStatus.NOTIFIED_WARE; // 已通知仓储
        }
        orderInfoMapper.updateOrderStatus(
                toStockTellUsStatus.getOrderStatus().name(),
                toStockTellUsStatus.name(),
                orderId,
                userId,
                Arrays.asList(fromPyidStatus.getOrderStatus().name()),
                Arrays.asList(fromPyidStatus.name())
        );
        log.info("订单【{}】 库存扣减状态【{}】 修改完成", orderId, toStockTellUsStatus.name());

        // 【电子面单业务】，进行发货&给物流发送消息==================================================================
        mqService.convertAndSend(MqConst.ORDER_EVENT_EXCHANGE,MqConst.ORDER_EXPRESS_RK,new OrderExpressMsg(orderId,userId));
    }


    @Override
    public List<ToWareOrderSpiltVo> splitOrder(Long orderId, String wareSkuMap) {
        // 1.由orderId查出【大订单】及【大订单的详情】
        OrderInfo orderInfo = getOrderInfo(orderId);
        List<OrderDetail> details = orderDetailService.getDetails(orderId, orderInfo.getUserId());
        // 2.将仓库传入的JSON转为对象 [{"wareId":"1","skuIds":["2","10"]},{"wareId":"2","skuIds":["3"]}]
        List<FromWareSkuMapVo> fromWareSkuMapVos = JSON.parseObject(wareSkuMap, new TypeReference<List<FromWareSkuMapVo>>() {
        });
        // 3.拆分小订单
        List<ToWareOrderSpiltVo> spiltVos = fromWareSkuMapVos.stream().map(vo -> {
            // 3.1 从JSON对应对象中得到【仓库id】，以及【该仓库下的商品id】
            String wareId = vo.getWareId();
            List<Long> skuIds = vo.getSkuIds().stream().map(item -> Long.parseLong(item)).collect(Collectors.toList());
            // 3.2 得到该仓库下的详情，即得到【小订单详情】
            List<OrderDetail> childDetails = details.stream().filter(item -> skuIds.contains(item.getSkuId())).collect(Collectors.toList());
            // 3.3 得到子订单&保存子订单
            OrderInfo childOrderInfo = getOrderInfo(orderId, orderInfo, childDetails);
            orderInfoService.save(childOrderInfo);
            // 3.4 存入子订单明细
            childDetails.stream().forEach(item -> {
                item.setId(null);
                item.setOrderId(childOrderInfo.getId());
            });
            orderDetailService.saveBatch(childDetails);
            // 3.5 将子订单数据封装为ToWareOrderSpiltVo
            ToWareOrderSpiltVo spiltVo = getToWareOrderSpiltVo(wareId, childDetails, childOrderInfo);
            return spiltVo;
        }).collect(Collectors.toList());
        log.info("拆单完成：详情：{}", spiltVos);
        // 4.大单状态修改为已拆分
        ProcessStatus fromPaidStatus = ProcessStatus.PAID;
        ProcessStatus toSplitStatus = ProcessStatus.SPLIT;
        orderInfoMapper.updateOrderStatus(toSplitStatus.getOrderStatus().name(),
                toSplitStatus.name(), orderInfo.getId(), orderInfo.getUserId(),
                Arrays.asList(fromPaidStatus.getOrderStatus().name()),
                Arrays.asList(fromPaidStatus.name()));
        return spiltVos;
    }

    /**
     * 秒杀服务准备的数据上进行处理
     * @param orderInfo
     * @return
     */
    @Override
    public Long insertSeckillOrder(OrderInfo orderInfo) {
        // 1.订单信息
        orderInfo.setOrderStatus(OrderStatus.UNPAID.name());
        orderInfo.setProcessStatus(ProcessStatus.UNPAID.name());
        orderInfo.setPaymentWay(PaymentWay.ONLINE.name());
        orderInfo.setOutTradeNo("MALL_"+System.currentTimeMillis() + "_" + orderInfo.getUserId());
        orderInfo.setTradeBody(orderInfo.getOrderDetails().get(0).getSkuName());
        orderInfo.setCreateTime(new Date());
        // 15min不支付就过期
        orderInfo.setExpireTime(new Date(System.currentTimeMillis() + 1000*60*15));
        orderInfo.setTrackingNo(""); //暂无
        orderInfo.setParentOrderId(0L); //暂无
        orderInfo.setImgUrl(orderInfo.getOrderDetails().get(0).getImgUrl());
        orderInfo.setOperateTime(new Date());
        orderInfo.setCouponAmount(new BigDecimal("0"));
        // 商品原价
        orderInfo.setOriginalTotalAmount(orderInfo.getOrderDetails().get(0).getOrderPrice());
        orderInfo.setFeightFee(new BigDecimal("0")); //运费暂无

        // 保存订单&保存后得到主键id
        orderInfoService.save(orderInfo);
        Long id = orderInfo.getId();

        //2、订单对应的商品明细
        List<OrderDetail> orderDetails = orderInfo.getOrderDetails();
        orderDetails.stream()
                .forEach(item-> item.setOrderId(id));
        orderDetailService.saveBatch(orderDetails);
        return id;
    }

    private ToWareOrderSpiltVo getToWareOrderSpiltVo(String wareId, List<OrderDetail> childDetails, OrderInfo childOrderInfo) {
        ToWareOrderSpiltVo spiltVo = new ToWareOrderSpiltVo();
        spiltVo.setOrderBody(childOrderInfo.getTradeBody());
        spiltVo.setConsignee(childOrderInfo.getConsignee());
        spiltVo.setOrderComment(childOrderInfo.getOrderComment());
        spiltVo.setWareId(wareId);
        spiltVo.setOrderId(childOrderInfo.getId().toString());
        spiltVo.setDeliveryAddress(childOrderInfo.getDeliveryAddress());
        List<ToWareOrderSpiltVo.DetailsDTO> dtoList = childDetails.stream().map(item -> {
            ToWareOrderSpiltVo.DetailsDTO dto = new ToWareOrderSpiltVo.DetailsDTO();
            dto.setSkuName(item.getSkuName());
            dto.setSkuId(item.getSkuId().toString());
            dto.setSkuNum(item.getSkuNum());
            return dto;
        }).collect(Collectors.toList());
        spiltVo.setDetails(dtoList);
        spiltVo.setPaymentWay("2"); //线上支付【这种代表数据库没设计好】
        return spiltVo;
    }

    private OrderInfo getOrderInfo(Long orderId, OrderInfo orderInfo, List<OrderDetail> childDetails) {
        OrderInfo childOrderInfo = new OrderInfo();
        childOrderInfo.setConsignee(orderInfo.getConsignee());
        childOrderInfo.setConsigneeTel(orderInfo.getConsigneeTel());
        // 【变化I】 下订单价格
        BigDecimal childOrderPrice = childDetails.stream().map(item -> item.getSplitTotalAmount()).reduce((x, y) -> x.add(y)).get();
        childOrderInfo.setTotalAmount(childOrderPrice);
        childOrderInfo.setOrderStatus(orderInfo.getOrderStatus());
        childOrderInfo.setUserId(orderInfo.getUserId());
        childOrderInfo.setPaymentWay(orderInfo.getPaymentWay());
        childOrderInfo.setDeliveryAddress(orderInfo.getDeliveryAddress());
        childOrderInfo.setOrderComment(orderInfo.getOrderComment());
        // 【变化II】 确保大订单的流水号唯一
        childOrderInfo.setOutTradeNo(orderInfo.getOutTradeNo() + "_child_" + UUID.randomUUID().toString().substring(0, 4));
        // 【变化III】 小单的商品名
        childOrderInfo.setTradeBody(childDetails.get(0).getSkuName());
        childOrderInfo.setCreateTime(orderInfo.getCreateTime());
        childOrderInfo.setExpireTime(orderInfo.getExpireTime());
        childOrderInfo.setProcessStatus(orderInfo.getProcessStatus());
        // 【变化III】小单独立的物流号-赞数不设置，下单出库才设置
        childOrderInfo.setTrackingNo("");
        // 【变化IV】
        childOrderInfo.setParentOrderId(orderId);
        // 【变化V】
        childOrderInfo.setImgUrl(childDetails.get(0).getImgUrl());
        // 【变化VI】
        childOrderInfo.setOperateTime(new Date());

        childOrderInfo.setActivityReduceAmount(new BigDecimal("0")); //无-忽略
        childOrderInfo.setCouponAmount(new BigDecimal("0")); //无-忽略

        // 【变化VII】原始总额
        childOrderInfo.setOriginalTotalAmount(childOrderPrice);

        // 【变化VIII】小单运费为0即可，大单一次扣除
        childOrderInfo.setFeightFee(new BigDecimal("0"));

        childOrderInfo.setRefundableTime(orderInfo.getRefundableTime());
        return childOrderInfo;
    }

    private OrderStatusLog prepareOrderStatus(OrderInfo orderInfo) {
        OrderStatusLog orderStatusLog = new OrderStatusLog();
        orderStatusLog.setUserId(orderInfo.getUserId());
        orderStatusLog.setOrderId(orderInfo.getId());
        orderStatusLog.setOrderStatus(orderInfo.getOrderStatus());
        orderStatusLog.setOperateTime(new Date());
        return orderStatusLog;
    }

    private List<OrderDetail> prepareOrderDetail(OrderInfo orderInfo, OrderSubmitVo orderSubmitVo) {
        List<OrderDetail> orderDetails = orderSubmitVo.getOrderDetailList().stream()
                .map(item -> {
                    OrderDetail orderDetail = new OrderDetail(); //明细id自动生成
                    orderDetail.setUserId(orderInfo.getUserId());
                    orderDetail.setOrderId(orderInfo.getId());
                    orderDetail.setSkuId(item.getSkuId());
                    orderDetail.setSkuName(item.getSkuName());
                    orderDetail.setImgUrl(item.getImgUrl());
                    orderDetail.setOrderPrice(item.getOrderPrice());
                    orderDetail.setSkuNum(item.getSkuNum());
                    orderDetail.setCreateTime(new Date());
                    // 实际支付金额 = 商品总额 - 优惠总额
                    orderDetail.setSplitTotalAmount(item.getOrderPrice().multiply(new BigDecimal(item.getSkuNum())));
                    // 促销分摊金额【优惠到某个商品而不是订单】
                    orderDetail.setSplitActivityAmount(new BigDecimal("0"));
                    // 优惠券金额
                    orderDetail.setSplitCouponAmount(new BigDecimal("0"));
                    return orderDetail;
                }).collect(Collectors.toList());
        return orderDetails;
    }

    private OrderInfo prepareOrderInfo(String tradeNo, OrderSubmitVo orderSubmitVo) {
        OrderInfo orderInfo = new OrderInfo();  //雪花算法已经处理过id了：orderInfo.setId(0L);
        // 用户信息
        orderInfo.setUserId(Long.valueOf(UserAuthUtil.getUserIdHeader()));
        orderInfo.setConsignee(orderSubmitVo.getConsignee());
        orderInfo.setConsigneeTel(orderSubmitVo.getConsigneeTel());
        orderInfo.setDeliveryAddress(orderSubmitVo.getDeliveryAddress());
        // 订单总金额
        BigDecimal totalAmount = orderSubmitVo.getOrderDetailList().stream()
                .map(item -> item.getOrderPrice().multiply(new BigDecimal(item.getSkuNum())))
                .reduce((x, y) -> x.add(y))
                .get();
        orderInfo.setTotalAmount(totalAmount);
        // 订单状态【给用户显示的大致状态】
        orderInfo.setOrderStatus(OrderStatus.UNPAID.name());
        // 支付方式
        orderInfo.setPaymentWay(PaymentWay.ONLINE.name());
        // 订单备注
        orderInfo.setOrderComment(orderSubmitVo.getOrderComment());
        // 对外交易号
        orderInfo.setOutTradeNo(tradeNo);
        // 订单体：订单描述，表订单买了哪些商品；可采用订单中第一个商品的名
        String skuName = orderSubmitVo.getOrderDetailList().get(0).getSkuName();
        orderInfo.setTradeBody("GMALL订单-" + skuName);
        // 创建时间
        orderInfo.setCreateTime(new Date());
        // 过期时间 30min 不支付就关闭订单【还可以根据用户等级过期】
        long closeTime = System.currentTimeMillis() + RedisConst.ORDER_TTL;
        orderInfo.setExpireTime(new Date(closeTime));
        // 订单的处理状态：给后台管理员显示的更精细的状态 已支付-（已支付/已通知仓库/库存异常等）
        orderInfo.setProcessStatus(ProcessStatus.UNPAID.name());
        // 订单图片
        String imgUrl = orderSubmitVo.getOrderDetailList().get(0).getImgUrl();
        orderInfo.setImgUrl(imgUrl);
        // 操作时间
        orderInfo.setOperateTime(new Date());
        // 原始金额 = 订单总额 + 优惠；没有优惠就等价于订单总额
        orderInfo.setOriginalTotalAmount(totalAmount);
        // 运费【对接第三方物流系统】【菜鸟(对接（申请账号，发送请求），快递鸟等平台)】
        orderInfo.setFeightFee(new BigDecimal("0"));
        // 签收后可退货日期
        orderInfo.setRefundableTime(new Date());
        /*
          orderInfo.setTrackingNo(""); 物流单号-未支付无需填写;
          orderInfo.setParentOrderId(0L); 父订单也暂不需要
          orderInfo.setProvinceId(0L); 发给哪个省
          // 优惠活动相关代码
          orderInfo.setActivityReduceAmount(new BigDecimal("0")); // 促销金额
          orderInfo.setCouponAmount(new BigDecimal("0")); //优惠券
         */
        return orderInfo;
    }
}
